package com.sneakxy.mybatis.commons.repository.query.domain;

import java.util.ArrayList;
import java.util.List;

import com.sneakxy.mybatis.commons.repository.query.domain.criterions.BetweenCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.CompareCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.EqCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.InableCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.NotEqCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.NullableCriterion;
import com.sneakxy.mybatis.commons.repository.query.domain.criterions.RegexCriterion;

/**
 * @author 潜行的青衣
 */
public class CriteriaBuilder<T> {

	private Class<T> loader;
	
	public static final String OR = "OR";
	
	public static final String AND = "AND";
	
	private List<Condition> conditions = new ArrayList<Condition>();
	
	public CriteriaBuilder(Class<T> loader) {
		this.loader = loader;
	}
	
	public CriteriaBuilder<T> orEq(String property, Object value) {
		return or(new EqCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> orEq(String property, Object value, String open, String close) {
		EqCriterion eq = new EqCriterion(loader, property, value);
		eq.setOpen(open);
		eq.setClose(close);
		return or(eq);
	}
	
	public CriteriaBuilder<T> andEq(String property, Object value) {
		return and(new EqCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> andEq(String property, Object value, String open, String close) {
		EqCriterion eq = new EqCriterion(loader, property, value);
		eq.setOpen(open);
		eq.setClose(close);
		return and(eq);
	}
	
	public CriteriaBuilder<T> orEq(String property, Object value, boolean ignoreCase) {
		return or(new EqCriterion(loader, property, value, ignoreCase));
	}
	
	public CriteriaBuilder<T> orEq(String property, Object value, boolean ignoreCase, String open, String close) {
		EqCriterion eq = new EqCriterion(loader, property, value, ignoreCase);
		eq.setOpen(open);
		eq.setClose(close);
		return or(eq);
	}
	
	public CriteriaBuilder<T> andEq(String property, Object value, boolean ignoreCase) {
		return and(new EqCriterion(loader, property, value, ignoreCase));
	}

	public CriteriaBuilder<T> andEq(String property, Object value, boolean ignoreCase, String open, String close) {
		EqCriterion eq = new EqCriterion(loader, property, value, ignoreCase);
		eq.setOpen(open);
		eq.setClose(close);
		return and(eq);
	}
	
	public CriteriaBuilder<T> orNotEq(String property, Object value) {
		return or(new NotEqCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> orNotEq(String property, Object value, String open, String close) {
		NotEqCriterion neq = new NotEqCriterion(loader, property, value);
		neq.setOpen(open);
		neq.setClose(close);
		return or(neq);
	}
	
	public CriteriaBuilder<T> andNotEq(String property, Object value) {
		return and(new NotEqCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> andNotEq(String property, Object value, String open, String close) {
		NotEqCriterion neq = new NotEqCriterion(loader, property, value);
		neq.setOpen(open);
		neq.setClose(close);
		return and(neq);
	}
	
	public CriteriaBuilder<T> orNotEq(String property, Object value, boolean ignoreCase) {
		return or(new NotEqCriterion(loader, property, value, ignoreCase));
	}
	
	public CriteriaBuilder<T> orNotEq(String property, Object value, boolean ignoreCase, String open, String close) {
		NotEqCriterion neq = new NotEqCriterion(loader, property, value, ignoreCase);
		neq.setOpen(open);
		neq.setClose(close);
		return or(neq);
	}
	
	public CriteriaBuilder<T> andNotEq(String property, Object value, boolean ignoreCase) {
		return and(new NotEqCriterion(loader, property, value, ignoreCase));
	}
	
	public CriteriaBuilder<T> andNotEq(String property, Object value, boolean ignoreCase, String open, String close) {
		NotEqCriterion neq = new NotEqCriterion(loader, property, value, ignoreCase);
		neq.setOpen(open);
		neq.setClose(close);
		return and(neq);
	}
	
	public CriteriaBuilder<T> orBetween(String property, Object startValue, Object endValue) {
		return or(new BetweenCriterion(loader, property, startValue, endValue));
	}
	
	public CriteriaBuilder<T> orBetween(String property, Object startValue, Object endValue, String open, String close) {
		BetweenCriterion bc = new BetweenCriterion(loader, property, startValue, endValue);
		bc.setOpen(open);
		bc.setClose(close);
		return or(bc);
	}
	
	public CriteriaBuilder<T> andBetween(String property, Object startValue, Object endValue) {
		return and(new BetweenCriterion(loader, property, startValue, endValue));
	}
	
	public CriteriaBuilder<T> andBetween(String property, Object startValue, Object endValue, String open, String close) {
		BetweenCriterion bc = new BetweenCriterion(loader, property, startValue, endValue);
		bc.setOpen(open);
		bc.setClose(close);
		return and(bc);
	}
	
	public CriteriaBuilder<T> orIsNull(String property) {
		return or(new NullableCriterion(loader, property, true));
	}
	
	public CriteriaBuilder<T> orIsNull(String property, String open, String close) {
		NullableCriterion nc = new NullableCriterion(loader, property, true);
		nc.setOpen(open);
		nc.setClose(close);
		return or(nc);
	}
	
	public CriteriaBuilder<T> andIsNull(String property) {
		return and(new NullableCriterion(loader, property, true));
	}
	
	public CriteriaBuilder<T> andIsNull(String property, String open, String close) {
		NullableCriterion nc = new NullableCriterion(loader, property, true);
		nc.setOpen(open);
		nc.setClose(close);
		return and(nc);
	}
	
	public CriteriaBuilder<T> orIsNotNull(String property) {
		return or(new NullableCriterion(loader, property, false));
	}
	
	public CriteriaBuilder<T> orIsNotNull(String property, String open, String close) {
		NullableCriterion nc = new NullableCriterion(loader, property, false);
		nc.setOpen(open);
		nc.setClose(close);
		return or(nc);
	}
	
	public CriteriaBuilder<T> andIsNotNull(String property) {
		return and(new NullableCriterion(loader, property, false));
	}
	
	public CriteriaBuilder<T> andIsNotNull(String property, String open, String close) {
		NullableCriterion nc = new NullableCriterion(loader, property, false);
		nc.setOpen(open);
		nc.setClose(close);
		return and(nc);
	}
	
	public CriteriaBuilder<T> orLt(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, "<"));
	}
	
	public CriteriaBuilder<T> orLt(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "<");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andLt(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, "<"));
	}
	
	public CriteriaBuilder<T> andLt(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "<");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orLte(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, "<="));
	}
	
	public CriteriaBuilder<T> orLte(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "<=");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andLte(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, "<="));
	}
	
	public CriteriaBuilder<T> andLte(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "<=");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orGt(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, ">"));
	}
	
	public CriteriaBuilder<T> orGt(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, ">");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andGt(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, ">"));
	}
	
	public CriteriaBuilder<T> andGt(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, ">");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orGte(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, ">="));
	}
	
	public CriteriaBuilder<T> orGte(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, ">=");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andGte(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, ">="));
	}
	
	public CriteriaBuilder<T> andGte(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, ">=");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orLike(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, "LIKE"));
	}
	
	public CriteriaBuilder<T> orLike(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "LIKE");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andLike(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, "LIKE"));
	}
	
	public CriteriaBuilder<T> andLike(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "LIKE");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orNotLike(String property, Object value) {
		return or(new CompareCriterion(loader, property, value, "NOT LIKE"));
	}
	
	public CriteriaBuilder<T> orNotLike(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "NOT LIKE");
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andNotLike(String property, Object value) {
		return and(new CompareCriterion(loader, property, value, "NOT LIKE"));
	}
	
	public CriteriaBuilder<T> andNotLike(String property, Object value, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "NOT LIKE");
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orLike(String property, Object value, boolean ignoreCase) {
		return or(new CompareCriterion(loader, property, value, "LIKE", ignoreCase));
	}
	
	public CriteriaBuilder<T> orLike(String property, Object value, boolean ignoreCase, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "LIKE", ignoreCase);
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andLike(String property, Object value, boolean ignoreCase) {
		return and(new CompareCriterion(loader, property, value, "LIKE", ignoreCase));
	}
	
	public CriteriaBuilder<T> andLike(String property, Object value, boolean ignoreCase, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "LIKE", ignoreCase);
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orNotLike(String property, Object value, boolean ignoreCase) {
		return or(new CompareCriterion(loader, property, value, "NOT LIKE", ignoreCase));
	}
	
	public CriteriaBuilder<T> orNotLike(String property, Object value, boolean ignoreCase, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "NOT LIKE", ignoreCase);
		cc.setOpen(open);
		cc.setClose(close);
		return or(cc);
	}
	
	public CriteriaBuilder<T> andNotLike(String property, Object value, boolean ignoreCase) {
		return and(new CompareCriterion(loader, property, value, "NOT LIKE", ignoreCase));
	}
	
	public CriteriaBuilder<T> andNotLike(String property, Object value, boolean ignoreCase, String open, String close) {
		CompareCriterion cc = new CompareCriterion(loader, property, value, "NOT LIKE", ignoreCase);
		cc.setOpen(open);
		cc.setClose(close);
		return and(cc);
	}
	
	public CriteriaBuilder<T> orIn(String property, Iterable<?> value) {
		return or(new InableCriterion(loader, property, value, true));
	}
	
	public CriteriaBuilder<T> orIn(String property, Iterable<?> value, String open, String close) {
		InableCriterion ic = new InableCriterion(loader, property, value, true);
		ic.setOpen(open);
		ic.setClose(close);
		return or(ic);
	}
	
	public CriteriaBuilder<T> andIn(String property, Iterable<?> value) {
		return and(new InableCriterion(loader, property, value, true));
	}
	
	public CriteriaBuilder<T> andIn(String property, Iterable<?> value, String open, String close) {
		InableCriterion ic = new InableCriterion(loader, property, value, true);
		ic.setOpen(open);
		ic.setClose(close);
		return and(ic);
	}
	
	public CriteriaBuilder<T> orNotIn(String property, Iterable<?> value) {
		return or(new InableCriterion(loader, property, value, false));
	}
	
	public CriteriaBuilder<T> orNotIn(String property, Iterable<?> value, String open, String close) {
		InableCriterion ic = new InableCriterion(loader, property, value, false);
		ic.setOpen(open);
		ic.setClose(close);
		return or(ic);
	}
	
	public CriteriaBuilder<T> andNotIn(String property, Iterable<?> value) {
		return and(new InableCriterion(loader, property, value, false));
	}
	
	public CriteriaBuilder<T> andNotIn(String property, Iterable<?> value, String open, String close) {
		InableCriterion ic = new InableCriterion(loader, property, value, false);
		ic.setOpen(open);
		ic.setClose(close);
		return and(ic);
	}
	
	public CriteriaBuilder<T> orRegex(String property, Object value) {
		return or(new RegexCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> orRegex(String property, Object value, String open, String close) {
		RegexCriterion eq = new RegexCriterion(loader, property, value);
		eq.setOpen(open);
		eq.setClose(close);
		return or(eq);
	}
	
	public CriteriaBuilder<T> andRegex(String property, Object value) {
		return and(new RegexCriterion(loader, property, value));
	}
	
	public CriteriaBuilder<T> andRegex(String property, Object value, String open, String close) {
		RegexCriterion eq = new RegexCriterion(loader, property, value);
		eq.setOpen(open);
		eq.setClose(close);
		return and(eq);
	}
	
	public CriteriaBuilder<T> or(Criterion criterion) {
		conditions.add(new Condition(OR, criterion));
		return this;
	}
	
	public CriteriaBuilder<T> and(Criterion criterion) {
		conditions.add(new Condition(AND, criterion));
		return this;
	}
	
	public Criteria<T> build() {
		return new CriteriaImpl<T>(this.conditions);
	}
	
}
